package com.mass.core.framework.web.filter;

import com.mass.core.utils.WebHelper;

import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.filter.CharacterEncodingFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;

/**
 * 框架上面得过滤器链. <p />
 * <pre>
 *     &lt;filter&gt;
 *         &lt;filter-name&gt;cherryFilterChain&lt;/filter-name&gt;
 *         &lt;filter-class&gt;org.springframework.web.filter.DelegatingFilterProxy&lt;/filter-class&gt;
 *         &lt;init-param&gt;
 *             &lt;param-name&gt;targetBeanName&lt;/param-name&gt;
 *             &lt;param-value&gt;cherryFilterChain&lt;/param-value&gt;
 *         &lt;/init-param&gt;
 *         &lt;init-param&gt;
 *             &lt;param-name&gt;targetFilterLifecycle&lt;/param-name&gt;
 *             &lt;param-value&gt;true&lt;/param-value&gt;
 *         &lt;/init-param&gt;
 *     &lt;/filter&gt;
 *     &lt;filter-mapping&gt;
 *         &lt;filter-name&gt;cherryFilterChain&lt;/filter-name&gt;
 *         &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
 *     &lt;/filter-mapping&gt;
 * </pre>
 * Created by tuyan on 15/8/25.
 */
public class CherryFilterChain extends CharacterEncodingFilter {

    private PathMatcher pathMatcher = new AntPathMatcher();

    /**
     * 是否包含工程路径.
     */
    private boolean includeContextPath = false;

    private String encoding;

    private boolean forceEncoding = false;

    /**
     * 是否使用xss脚本清理.
     */
    private boolean cleanXss = false;

    /**
     * 可匿名访问请求.
     */
    private String[] anonymousURIs;

    /**
     * 可访问的资源文件请求.
     */
    private String[] resourceURIs;

    /**
     * 框架过滤器链.
     */
    private List<CherryFilter> filters = new ArrayList<CherryFilter>();

    /**
     * 提前执行的框架过滤器链，在过滤可匿名访问请求之前执行.
     */
    private List<CherryFilter> preFilters = new ArrayList<CherryFilter>();


    /**
     * Set the encoding to use for requests. This encoding will be passed into a
     * {@link javax.servlet.http.HttpServletRequest#setCharacterEncoding} call.
     * <p>Whether this encoding will override existing request encodings
     * (and whether it will be applied as default response encoding as well)
     * depends on the {@link #setForceEncoding "forceEncoding"} flag.
     */
    public void setEncoding(String encoding) {
        this.encoding = encoding;
    }

    /**
     * Set whether the configured {@link #setEncoding encoding} of this filter
     * is supposed to override existing request and response encodings.
     * <p>Default is "false", i.e. do not modify the encoding if
     * {@link javax.servlet.http.HttpServletRequest#getCharacterEncoding()}
     * returns a non-null value. Switch this to "true" to enforce the specified
     * encoding in any case, applying it as default response encoding as well.
     */
    public void setForceEncoding(boolean forceEncoding) {
        this.forceEncoding = forceEncoding;
    }

    public void setIncludeContextPath(boolean includeContextPath) {
        this.includeContextPath = includeContextPath;
    }

    
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        if (this.cleanXss) {
            // xss脚本敏感字符清理
            request = new XssHttpServletRequestWrapper(request);
        }
        // 首先进行编码过滤
        doCharacterEncodingFilter(request, response);
        // 之后再进行业务逻辑过滤
        String requestURI = WebHelper.getRequestURIWithSuffix(request, includeContextPath);
        // 判断是否为匿名访问uri
        if (isResourceURI(requestURI)) {
            // 可以匿名访问的URI直接通过过滤器
            filterChain.doFilter(request, response);
        } else {
            boolean isSentResponse = false;
            for (CherryFilter preFilter : preFilters) {
                preFilter.doFilter(request, response);
                if (preFilter.isSentResponse()) {
                    isSentResponse = true;
                    break;
                }
            }
            if (!isSentResponse) {
                if (!isAnonymousURI(requestURI)) {
                    for (CherryFilter lollipopFilter : filters) {
                        lollipopFilter.doFilter(request, response);
                        if (lollipopFilter.isSentResponse()) {
                            isSentResponse = true;
                            break;
                        }
                    }
                    // 通过过滤器
                    if (!isSentResponse) {
                        filterChain.doFilter(request, response);
                    }
                } else {
                    filterChain.doFilter(request, response);
                }
            }
        }
    }

    private void doCharacterEncodingFilter(HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException {
        if (this.encoding != null && (this.forceEncoding || request.getCharacterEncoding() == null)) {
            request.setCharacterEncoding(this.encoding);
            if (this.forceEncoding) {
                response.setCharacterEncoding(this.encoding);
            }
        }
    }

    /**
     * 是否为可访问的资源文件URI.
     *
     * @param uri   要判断的URI
     * @return  如果是则返回True
     */
    private boolean isResourceURI(String uri) {
        if (null == resourceURIs || 0 == resourceURIs.length) {
            return false;
        }
        for (String resourceURI : resourceURIs) {
            if (pathMatcher.match(resourceURI, uri)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 是否为可匿名访问URI.
     *
     * @param uri   要判断的URI
     * @return  如果是则返回True
     */
    private boolean isAnonymousURI(String uri) {
        if (null == anonymousURIs || 0 == anonymousURIs.length) {
            return false;
        }
        for (String anonymousURI : anonymousURIs) {
            if (pathMatcher.match(anonymousURI, uri)) {
                return true;
            }
        }
        return false;
    }

    public void setCleanXss(boolean cleanXss) {
        this.cleanXss = cleanXss;
    }

    public void setAnonymousURIs(String[] anonymousURIs) {
        this.anonymousURIs = anonymousURIs;
    }

    public void setResourceURIs(String[] resourceURIs) {
        this.resourceURIs = resourceURIs;
    }

    public void setFilters(List<CherryFilter> filters) {
        this.filters = filters;
    }

	public void setPreFilters(List<CherryFilter> preFilters) {
		this.preFilters = preFilters;
	}
}
